package com.googlecode.imanager.swing.form;

import java.util.List;
import java.util.Vector;

import com.googlecode.imanager.client.bridge.form.IInputList;
import com.googlecode.imanager.client.form.InputList;
import com.googlecode.imanager.client.value.format.Formatter;
import com.googlecode.imanager.core.base.BridgeApplication;

/**
 * @author wellsantos@wellsantos.com
 * @since 0.01
 */
public abstract class SwingInputList<T> extends SwingInputControl implements IInputList<T> {

    private InputListModel model;
    private int itemCount;
    
    public SwingInputList(BridgeApplication bridge, InputList<T> source) {
        super(bridge, source);
    }
    
    @Override
    protected void buildImplementation() {
        getModel();
        super.buildImplementation();
    }

    @Override
    @SuppressWarnings("unchecked")
    public InputList<T> getSource() {
        return (InputList<T>) source;
    }

    protected InputListModel createModel(){
        return new InputListModel();
    }

    public InputListModel getModel() {
        if (model == null){
            model = createModel();
        }
        return model;
    }

    @Override
    public void setFormatter(Formatter<T> formatter) {
        setItems(getSource().getItems());
    }

    @Override
    public void setItems(List<T> items) {
        int itemCount = this.itemCount;
        int dif = items.size() - itemCount;
        this.itemCount = items.size();
            
        if (dif > 0){
            getModel().fireIntervalAdded(itemCount, items.size());
        } 
        else if (dif < 0){
            getModel().fireIntervalRemoved(items.size(), itemCount);
        }
        
        getModel().fireContentsChanged(0, itemCount-1);
    }

    @Override
    public void addItem(int index, T item) {
        itemCount++;
        getModel().fireIntervalAdded(index, index);
    }

    @Override
    public void removeItem(int index) {
        itemCount--;
        getModel().fireIntervalRemoved(index, index);
    }

    @Override
    public void setItem(int index, T item) {
        getModel().fireContentsChanged(index, index);
    }

    @Override
    public void setSelectedIndex(int selectedIndex) {
        getModel().fireContentsChanged(-1, -1);
    }

    
    /**
     * @author wellsantos@wellsantos.com
     * @since 0.01
     */
    public class InputListModel extends javax.swing.AbstractListModel
                                implements javax.swing.MutableComboBoxModel {
        
        private static final long serialVersionUID = 1L;
        
        private Vector<ItemValue> cachedValues;
        
        public InputListModel() {
            cachedValues = new Vector<ItemValue>();
        }

        // implements javax.swing.ComboBoxModel
        @SuppressWarnings("unchecked")
        public void setSelectedItem(Object anObject) {
            if (anObject instanceof ItemValue){
                anObject = ((ItemValue)anObject).getSource();
            }
            getSource().setSelectedItem((T) anObject);
        }

        // implements javax.swing.ComboBoxModel
        public Object getSelectedItem() {
            return getElementAt(getSource().getSelectedIndex());
        }

        // implements javax.swing.ListModel
        public int getSize() {
            return itemCount;
        }

        // implements javax.swing.ListModel
        public Object getElementAt(int index) {
            if ( index >= 0 && index < getSource().getItems().size() ){
                ItemValue v = cachedValues.get(index);
                if (v == null){
                    v = new ItemValue(getSource().getItems().get(index), 
                                      getSource().getItemText(index));
                    cachedValues.set(index, v);
                }
                return v;
            } else {
                return null;
            }
        }

        // implements javax.swing.MutableComboBoxModel
        @SuppressWarnings("unchecked")
        public void addElement(Object anObject) {
            if (anObject instanceof ItemValue){
                anObject = ((ItemValue)anObject).getSource();
            }
            getSource().getItems().add((T) anObject);
        }

        // implements javax.swing.MutableComboBoxModel
        @SuppressWarnings("unchecked")
        public void insertElementAt(Object anObject, int index) {
            if (anObject instanceof ItemValue){
                anObject = ((ItemValue)anObject).getSource();
            }
            getSource().getItems().add(index, (T) anObject);
        }

        // implements javax.swing.MutableComboBoxModel
        public void removeElementAt(int index) {
            getSource().getItems().remove(index);
        }

        // implements javax.swing.MutableComboBoxModel
        public void removeElement(Object anObject) {
            if (anObject instanceof ItemValue){
                anObject = ((ItemValue)anObject).getSource();
            }
            int index = getSource().getItems().indexOf(anObject);
            if ( index != -1 ) {
                removeElementAt(index);
            }
        }

        protected void fireContentsChanged(int index0, int index1) {
            if (index0 != -1){
                for (int i=index0; i<=index1; i++){
                    ItemValue item = cachedValues.get(i);
                    if (item != null){
                        item.setDisplayValue(getSource().getItemText(i));
                    }
                }
            }
            super.fireContentsChanged(this, index0, index1);
        }

        protected void fireIntervalAdded(int index0, int index1) {
            for (int i=index0; i<=index1; i++){
                cachedValues.add(i, null);
            }
            super.fireIntervalAdded(this, index0, index1);
        }

        protected void fireIntervalRemoved(int index0, int index1) {
            for (int i=index1; i>=index0; i--){
                cachedValues.remove(i);
            }
            super.fireIntervalRemoved(this, index0, index1);
        }

    }
    
    /**
     * @author wellsantos@wellsantos.com
     * @since 0.01
     */
    private static class ItemValue {
        
        private Object source;
        private String displayValue;
        
        public ItemValue(Object source, String displayValue) {
            this.setSource(source);
            this.setDisplayValue(displayValue);
        }

        public Object getSource() {
            return source;
        }

        public void setSource(Object source) {
            this.source = source;
        }

        public String getDisplayValue() {
            return displayValue;
        }

        public void setDisplayValue(String displayValue) {
            this.displayValue = displayValue == null ? "" : displayValue;
        }

        @Override
        public String toString() {
            return getDisplayValue();
        }
        
    }
    
}
